﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using PersistenceCommons;
using Utils;

using NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;

namespace Controller
{
    public class AppController
    {

        private Configuration configuration = new NHibernate.Cfg.Configuration().Configure();
        


        private SerialPort mSerialPort = new SerialPort();

        private Utils.GlobalUtilities util = new GlobalUtilities();

        private LoginUser analysisLoginUser;


        const int dataBlockSize = 11;
        const int dataBlockOffSet = 0;
        const int dataObjectComponent = 4;
        const int dataQuality1OffSet = 5;
        const int dataQuality2OffSet = 6;
        const int dataQuality3OffSet = 7;
        const int dataQuality4OffSet = 8;
        const int dataFlowControlByte = 4;
        const int dataCheckOffSet = 9;


        byte[] lastDataByteReceived = new byte[dataBlockSize];
        byte[] dataSync = new byte[dataBlockSize];
        byte[] dataIden = new byte[dataBlockSize];

        byte[] startSync = { 0xAA, 0x55, 0x41, 0x05, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x01, 0x52 };
        byte[] startIden = { 0xAA, 0x55, 0x41, 0x05, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92 };
        byte[] startAnal = { 0xAA, 0x55, 0x41, 0x05, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50 };

        byte[] resendResult = { 0xAA, 0x55, 0x41, 0x05, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82 };
        

        byte ackbyte = 0x1D;
        byte ackidnt = 0x5D;

        byte endAnal = 0x2D;
        byte reSendB = 0x3D;
        byte errAnal = 0x0C;
        byte errEmpt = 0xE0;
        byte errCali = 0xE1;
        byte errTmp1 = 0xE3;
        byte errTmp2 = 0xE4;
        byte errGene = 0xE5;
        byte errProt = 0xE6;
        byte errCSum = 0xE7;

        public AppController ()
        {
            mSerialPort.PortName = "COM4";
            mSerialPort.BaudRate = 19200;
            mSerialPort.DataBits = 8;
            mSerialPort.Handshake = System.IO.Ports.Handshake.None;
            mSerialPort.StopBits = System.IO.Ports.StopBits.One;
        }

        public AnalysisStatus startAnalysis()
        {
           
            serialPortOpen();

            //mSerialPort.DataReceived += new SerialDataReceivedEventHandler(mSerialPortDataReceived);
            
            //Envia o primeiro quadro para Sincronizar com o hardware
            mSerialPort.Write(startSync, dataBlockOffSet, dataBlockSize);

            //Verifica o buffer da porta serial até conter os bytes para preenchimento de um cloco de comunicação
            readBlockData(dataSync, dataBlockOffSet, dataBlockSize);

            //TODO: realizar checksum do dado recebido

            if (dataSync[dataFlowControlByte] == ackbyte)
            {
                serialPortOpen();
                //Envia o quadro de identificação
                mSerialPort.Write(startIden, dataBlockOffSet, dataBlockSize);
                //Aguarda preenchimento do buffer
                readBlockData(dataIden, dataBlockOffSet, dataBlockSize);

                if (dataIden[dataFlowControlByte] == ackidnt & (dataIden[5] == 0x00 & dataIden[6] != 0x00))
                {
                                 
                    return AnalysisStatus.AnalysisStarted;
                }

                else
                {
                   
                    return AnalysisStatus.FirmwareVersionError;                
                }
            }

            else
            {
                if (dataSync[dataQuality1OffSet] == errEmpt)
                {
                    return AnalysisStatus.EmptySample;
                }

                if (dataSync[dataQuality1OffSet] == errCali)
                {
                    return AnalysisStatus.ErrorCalibration;
                }

                if (dataSync[dataQuality1OffSet] == errTmp1)
                {
                    return AnalysisStatus.Temp1Error;
                }

                if (dataSync[dataQuality1OffSet] == errTmp2)
                {
                    return AnalysisStatus.Temp2Error;
                }

                if (dataSync[dataQuality1OffSet] == errGene)
                {
                    return AnalysisStatus.GeneralError;
                }

                return AnalysisStatus.SyncError;
            }
        }

        public AnalysisObject Analysis(DateTime factoryDateIn, string analysisObjectPIDIn, string analysisObjectPackagePIDIn)
        {
            //Configuration configuration = new NHibernate.Cfg.Configuration().Configure();
            ISessionFactory factory = configuration.BuildSessionFactory();
            
            //new NHibernate.Tool.hbm2ddl.SchemaExport(configuration).Create(true, true);

            using (ISession session = factory.OpenSession())
            using (ITransaction t = session.BeginTransaction())
            {

                AnalysisObject ObjectInAnalysis = new AnalysisObject();
                ObjectInAnalysis.analysisUser = analysisLoginUser;
                ObjectInAnalysis.analysisDate = DateTime.Now;
                ObjectInAnalysis.factoryDate = factoryDateIn;
                ObjectInAnalysis.analysisObjectPID = analysisObjectPIDIn;
                ObjectInAnalysis.analysisObjectPackagePID = analysisObjectPackagePIDIn;

                serialPortOpen();
                //Envia comando para o hardware iniciar a Análise
                mSerialPort.Write(startAnal, dataBlockOffSet, dataBlockSize);


                while (lastDataByteReceived[dataFlowControlByte] != endAnal & lastDataByteReceived[dataQuality1OffSet] != errEmpt & lastDataByteReceived[dataQuality1OffSet] != errCali & lastDataByteReceived[dataQuality1OffSet] != errTmp1 & lastDataByteReceived[dataQuality1OffSet] != errTmp2 & lastDataByteReceived[dataQuality1OffSet] != errGene)
                {
                    serialPortOpen();
                    readBlockData(lastDataByteReceived, dataBlockOffSet, dataBlockSize);

                    if (lastDataByteReceived[dataFlowControlByte] != endAnal & lastDataByteReceived[dataQuality1OffSet] != errEmpt & lastDataByteReceived[dataQuality1OffSet] != errCali & lastDataByteReceived[dataQuality1OffSet] != errTmp1 & lastDataByteReceived[dataQuality1OffSet] != errTmp2 & lastDataByteReceived[dataQuality1OffSet] != errGene)
                    {
                        AnalysisObjectComponent inBoundComponent = new AnalysisObjectComponent();
                        inBoundComponent.analysisObject = ObjectInAnalysis;
                        inBoundComponent.analysisObjectComponentPID = util.getAnalysisItem(lastDataByteReceived, dataObjectComponent);
                        inBoundComponent.analysisQualityResultP1 = util.byteToValue(lastDataByteReceived, dataQuality1OffSet, dataQuality2OffSet);
                        inBoundComponent.analysisQualityResultP2 = util.byteToValue(lastDataByteReceived, dataQuality3OffSet, dataQuality4OffSet);
                        ObjectInAnalysis.AddAnalysisObject(inBoundComponent);
                    }

                }
                
                if (ObjectInAnalysis.analysisObjectComponent.Count > 0)
                {
                    session.Save(ObjectInAnalysis);
                    t.Commit();
                }

                mSerialPort.Close();
                return ObjectInAnalysis;
            }
            
        
        } 

         public byte[] readBlockData(byte[] dataBlock, int blockOffSet,int blockSize)
         {
             while (mSerialPort.BytesToRead < blockSize)
             {
             }            
           
              mSerialPort.Read(dataBlock, blockOffSet, blockSize);

              if (util.checkDataSum(dataBlock, dataCheckOffSet))
              {
                  return dataBlock;
              }

              else
              {
                  mSerialPort.Write(resendResult, dataBlockOffSet, dataBlockSize);
                  return readBlockData(dataBlock, blockOffSet, blockSize);
              }
         
         }

         public void mSerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
         {

             if (mSerialPort.BytesToRead > dataBlockSize)
             {
                 mSerialPort.Read(lastDataByteReceived, dataBlockOffSet, dataBlockSize);

             }

         }

       

         public void serialPortOpen()
         {
             if  (!mSerialPort.IsOpen) 
             {
                 mSerialPort.Open();
             }
         }

         public bool login(string LoginUserID, string LoginUserPIN)
         {


             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<LoginUser>();
                 var loginUser = criteria.
                     Add(NHibernate.Criterion.Expression.Eq("loginUserID", LoginUserID)).
                     Add(NHibernate.Criterion.Expression.Eq("loginUserPIN", LoginUserPIN)).
                     List<LoginUser>();
                 if (loginUser.Count == 1)
                 {
                     this.analysisLoginUser = loginUser.First();
                     return true;
                 }
                 else 
                 {
                     return false;
                 }
             }
            
         }

         public IList<AnalysisObject> retrieveAnalyzedObjectPackagePID(string code)
         {
             IList<AnalysisObject> returned = new List<AnalysisObject>();
             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<AnalysisObject>();
                 var analysisObjects = criteria.
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPackagePID", code)).
                     Add(NHibernate.Criterion.Expression.Eq("analysisUser", analysisLoginUser)).
                     List<AnalysisObject>();
                 
                     returned = analysisObjects;
                 
             }
             return returned;
         }

         public IList<AnalysisObject> retrieveAnalyzedObjectBySerial(string serial)
         {
             IList<AnalysisObject> returned = new List<AnalysisObject>();
             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<AnalysisObject>();
                 var analysisObjects = criteria.
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPID", serial)).
                     Add(NHibernate.Criterion.Expression.Eq("analysisUser", analysisLoginUser)).
                     List<AnalysisObject>();

                 returned = analysisObjects;

             }
             return returned;
         }

         public IList<AnalysisObject> retrieveAnalyzedObjectByFactoryDate(DateTime factoryDt)
         {
             IList<AnalysisObject> returned = new List<AnalysisObject>();
             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<AnalysisObject>();
                 var analysisObjects = criteria.
                     Add(NHibernate.Criterion.Expression.Ge("factoryDate", factoryDt)).
                     Add(NHibernate.Criterion.Expression.Lt("factoryDate", factoryDt.AddDays(1))).
                     Add(NHibernate.Criterion.Expression.Eq("analysisUser", analysisLoginUser)).
                     List<AnalysisObject>();

                 returned = analysisObjects;

             }
             return returned;
         }

         public IList<AnalysisObject> retrieveAnalyzedObject(string code, string serial, DateTime factoryDt)
         {
             IList<AnalysisObject> returned = new List<AnalysisObject>();
             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<AnalysisObject>();
                 var analysisObjects = criteria.
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPackagePID", code)).
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPID", serial)).
                     Add(NHibernate.Criterion.Expression.Ge("factoryDate", factoryDt)).
                     Add(NHibernate.Criterion.Expression.Lt("factoryDate", factoryDt.AddDays(1))).
                     Add(NHibernate.Criterion.Expression.Eq("analysisUser", analysisLoginUser)).
                     List<AnalysisObject>();

                 returned = analysisObjects;

             }
             return returned;
         }

         public IList<AnalysisObject> retrieveAnalyzedObjectNoDate(string code, string serial)
         {
             IList<AnalysisObject> returned = new List<AnalysisObject>();
             ISessionFactory factory = configuration.BuildSessionFactory();
             using (ISession session = factory.OpenSession())
             {

                 ICriteria criteria = session.CreateCriteria<AnalysisObject>();
                 var analysisObjects = criteria.
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPackagePID", code)).
                     Add(NHibernate.Criterion.Expression.Eq("analysisObjectPID", serial)).
                     Add(NHibernate.Criterion.Expression.Eq("analysisUser", analysisLoginUser)).
                     List<AnalysisObject>();

                 returned = analysisObjects;

             }
             return returned;
         }

    }
}